查看原文
其他

Android 14 彻底终结大厂流氓应用

程序员DHL ByteCode 2023-12-27

hi 这是 dhl 的第 90 篇文章

个人微信: hi-dhl

Hi 大家好,我是 DHL,大厂程序员,就职于 美团、快手、小米。分享技术干货和编程知识点

在某些大厂内部通常都会有一个神秘的团队,他们的工作内容就是专门研究系统,而的事情就是如何让自家应用在后台存活的更久,达到永生的目的。

其中有个别公司,甚者利用公开漏洞,达到远程操控用户手机的目的,做更多他们想做的事,可以随意获取用户的隐私,而且一旦安装,普通用户很难删除,之前写了一些揭露他们的文章,但是现在已经被全部删除了,就连评论区抨击他们的内容也全都被删除了。

而 Android 14 的出现,可以说是暂时性的彻底终结了这些流氓软件,想在后台通过保活的方式,让应用在后台达到永生的目的基本不可能了。

为什么这是暂时性的呢,因为没有完美的系统,新的系统虽然修复了公开漏洞,终结了现有的保活的方式,但是新系统可能存在新的漏洞,还是会给某些大厂可乘之机。

我们一起来看一下 Android 工程副总裁 Dave Burke 都介绍 Andorid 14 在性能、隐私、安全性方面做了那些改进,这篇文章是对之前的文章 适配 Android 14,你的应用受影响了吗  和 Android 14 新增权限 的补充。

  • 冻结缓存应用,增强杀进程能力

  • 应用启动更快

  • 减少内存占用

  • 屏幕截图检查

  • 显示全屏系统通知

  • 精确闹钟权限

  • 提供了对照片和视频的部分访问权限

  • 最小 targetSdkVersion 限制

  • 返回手势

本文只会介绍我认为 Android 14 上最大的变化,关于 Android 14 的所有变更,可以前往查看变更https://developer.android.com/about/versions/14

冻结缓存应用,增强杀进程能力

在 Android 11 以上支持冻结已缓存的应用,当应用切换到后台并且没有其他活动时,系统会在一定时间内通过状态判断,是否冻结该应用,如果一个应用被冻结住了,将完全被 "暂停",不再消耗任何 CPU 资源,可以减少应用在后台消耗的 CPU 资源,从而达到节电的目的。

被冻结已缓存的应用并不会执行终止该应用,冻结的作用只是暂时挂起进程,消耗 CPU 的资源为 0,它有助于提高系统性能和稳定性,同时最大限度地节省设备的资源和电量的消耗,一旦应用再次切换到前台时,系统会将该应用的进程解冻,实现快速启动。

如果你的手机支持冻结已缓存的应用,在开发者选项里会显示 「暂停执行已缓存的应用」设置项。

冻结已缓存应用,在内核层面使用的是 cgroup v2 freezer,相对于使用信号 SIGSTOP 与 SIGCONT 实现的挂起与恢复,cgroup v2 freezer 无法被拦截,也就无法被开发者 Hook,从而彻底终结大厂想在这个基础上做一些事情。

当然 Google 也对 cgroup 进行了封装,提供了 Java API,在上层我们也可以调用对应的方法实现 CPU、内存资源的控制。

public static final native void setProcessFrozen(int pid, int uid, boolean frozen);
public static final native void enableFreezer(boolean enable);

经过测试 Android 14 相比于 Android 13,缓存进程的 CPU 使用量降低了高达 50%,因此,除了传统的 Android 应用生命周期 API,如前台服务、JobScheduler 或 WorkManager,后台工作将受到限制。

另外在 Android 14 上系统在杀进程之前,首先会将应用所有的进程进行 cgroup v2 freezer,被冻结的应用 cpu 资源占用为 0,然后在挨个杀掉进程,想通过进程互相拉取进程的方式,不断的想通过 fork 出子进程,达到应用永生的目的,在 Android 14 以上已经不可能了,这些黑科技都要告别历史的舞台了。

应用启动更快

在 Android 14 上对缓存应用进行优化,增加了缓存应用的最大数量的限制,从而减少了冷启动应用的次数。

而应用的最大缓存数量不是固定的,可以根据设备的内存容量进行调整,Android 测试团队在 8GB 设备上,发现冷启动应用的数量减少了 20%,而在 12GB 设备上减少了超过 30%,冷启动相对于热启动来说速度较慢,而且在电量消耗方面成本较高,这一工作有效地改善了电量使用和整体应用启动时间。

减少内存占用

代码大小是我们关注的关键指标之一,代码量越大虚拟内存占用越高,减少生成的代码大小,对内存(包括 RAM 和存储空间)的影响就越小。

在 Android 14 中,改进 Android 运行时(ART)对 Android 用户体验,ART 包含了优化措施,将代码大小平均减少了 9.3%,而不会影响性能。

屏幕截图检查

在 Android 14 中新增了一个特殊的 API,截取屏幕截图后会有个 ScreenCaptureCallback 的回调,当用户正在使用截取屏幕截图时,将会调用这些回调函数。

要使 API 正常工作,需要在 AndroidManifest 中添加 DETECT_SCREEN_CAPTURE 权限,然后在 onStart() 方法中注册回调,需要在 onStop() 中取消注册。

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
   <uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
</manifest>


class MainActivity : Activity() {

   private val mainExecutor = MainEcxector()
   
   private val screenshotCallback = ScreenCaptureCallback {
       // A screenshot was taken
  }

   override fun onStart() {
       super.onStart()
       registerScreenCaptureCallback(mainExecutor, screenshotCallback)
  }

   override fun onStop() {
       super.onStop()
       unregisterScreenCaptureCallback(screenshotCallback)
  }
}

显示全屏系统通知

Android 11 引入了全屏通知,当全屏应用程序运行时,这些通知将在锁屏屏幕上显示,任何应用都可以在手机处于锁定状态时使用 Notification. Builder. setFullScreenIntent 发送全屏 Intent,不过需要在  AndroidManifest 中声明 USE_FULL_SCREEN_INTENT 权限,在应用安装时自动授予此权限。

从 Android 14 开始,使用此权限的应用仅限于提供通话和闹钟的应用。对于不适合此情况的任何应用,Google Play 商店会撤消其默认的 USE_FULL_SCREEN_INTENT 权限。

在用户更新到 Android 14 之前,在手机上已经安装的应用仍拥有此权限,但是用户可以开启和关闭此权限,所以您可以使用新 API NotificationManager.canUseFullScreenIntent 检查应用是否具有该权限。

如果想在 Android 14 上使用这个权限,我们可以提示用户手动打开授权,通过 Intent(ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT) 来跳转到设置界面。

if(NotificationManager.canUseFullScreenIntent()){
   startActivity(Intent(ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT))
}

精确闹钟权限

在 Andorid 12 之前我们可以直接调用 setAlarmClock()setExact()setExactAndAllowWhileIdle() 等等方法设置精确闹钟时间,

但是在 Android 12 上 Google 引入了一个新的权限 SCHEDULE_EXACT_ALARM,如果想调用 setAlarmClock()setExact()setExactAndAllowWhileIdle() 等等方法设置精确闹钟时间, 需要在 manifest 中申明  android.permission.SCHEDULE_EXACT_ALARM 权限。

所以运行在 Android 12 ~ Android 13 系统上,我们只需要声明一下权限就可以使用了,但是从 Android 14 开始 SCHEDULE_EXACT_ALARM 权限默认被禁止使用了。

如果你还想在 Andorid 14 以上使用精准闹钟的 API,我们可以提示用户手动打开授权,通过 Intent (ACTION_REQUEST_SCHEDULE_EXACT_ALARM) 来跳转到设置界面,代码如下。

val alarmManager: AlarmManager = context.getSystemService<AlarmManager>()!!
when {
  // If permission is granted, proceed with scheduling exact alarms.
  alarmManager.canScheduleExactAlarms() -> {
      alarmManager.setExact(...)
  }
  else -> {
      // Ask users to go to exact alarm page in system settings.
      startActivity(Intent(ACTION_REQUEST_SCHEDULE_EXACT_ALARM))
  }
}

提供了对照片和视频的部分访问权限

这个限制和 iOS 相似,Android 14 提供了对照片和视频的部分访问权限。当您访问媒体数据时,用户将看到一个对话框,提示用户授予对所有媒体的访问、或者授予单个照片/视频的访问权限,该新功能将适用于 Android 14 上所有应用程序,无论其 targetSdkVersion 是多少。

在 Android 13 上已经引入了单独的照片访问和视频访问权限,但是在 Android 14 上新增了新的权限 READ_MEDIA_VISUAL_USER_SELECTED

<manifest xmlns:android="http://schemas.android.com/apk/res/android" />

   <!-- Devices running Android 13 (API level 33) or higher -->
   <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
   <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

   <!-- To handle the reselection within the app on Android 14 -->
   <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />

</manifest>

如果没有声明新的权限,当应用程序进入后台或用户终止应用程序时,单独的照片访问和视频访问权限将立即撤销,不会保存 READ_MEDIA_IMAGES 和  READ_MEDIA_VIDEO 权限的状态,每次都需要检查。

最小 targetSdkVersion 限制

Android 14 当中有一个比较大的变化就是,无法安装 targetSdk <= 23 的应用程序 (Android 6.0),不要将它与 minSdk 混淆。

在 Android 开发中有两个比较重要的版本号:

  • compileSdkVersion :用于编译当前项目的 Android 系统版本

  • targetSdkVersion :App 已经适配好的系统版本,系统会根据这个版本号,来决定是否可以使用新的特性

这个最小 targetSdkVersion 限制,主要是出于安全考虑,在 Android 6.0 中引入了运行时权限机制,App 想要使用一些敏感权限时,必须先弹窗询问用户,用户点击允许之后才能使用相应的权限。

但是一些 App 为了利用权限方便获取到用户的信息,通过不去升级 targetSdk 的版本号的方式,在安装过程中获得所有权限,以最低成本的方式,绕过运行时权限机制。

如果之前已经安装了的 App,就算升级到 Android 14 也会去保留,系统不能代表用户去删除某个应用,其实我在想,为什么不针对这些已经安装好的低版本的 App,Google 给出一些警告提示,让用户可以感知到呢

返回手势

在 Android 13 的时候,Google 已经预示我们在下一个版本中,返回手势将会有一些更新,并以预览屏幕的形式呈现动画,效果如下图所示。

我们来演示一下使用后退导航的动画。

在 Android 14 增加了在 App 中创建过渡动画的功能,比如在 OnBackPressedCallback 接口中添加了一个方法 handleonbackprogress() ,这个方法在返回手势执行过程中被调用,我们可以在这个方法中增加一些过渡动画。

OnBackPressedCallback 接口中还提供了两个方法 handleOnBackPressed()handleOnBackCancelled()  分别在动画完成和取消动画时调用,我们来看看在代码中如何使用。

class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
      ...
       val box = findViewById<View>(R.id.box)
       val screenWidth =
           Resources.getSystem().displayMetrics.widthPixels
       val maxXShift = (screenWidth / 20)

       val callback = object : OnBackPressedCallback(
           enabled = true
      ) {

           override fun handleOnBackProgressed(
               backEvent: BackEvent
          ) {
               when (backEvent.swipeEdge) {
                   BackEvent.EDGE_LEFT ->
                       box.translationX = backEvent.progress *    
                           maxXShift
                   BackEvent.EDGE_RIGHT ->
                       box.translationX = -(backEvent.progress *
                           maxXShift)
              }
               box.scaleX = 1F - (0.1F * backEvent.progress)
               box.scaleY = 1F - (0.1F * backEvent.progress)
          }

           override fun handleOnBackPressed() {
               // Back Gesture competed
          }

           
           override fun handleOnBackCancelled() {
               // Back Gesture cancelled
               // Reset animation objects to initial state
          }
      }
       this.onBackPressedDispatcher.addCallback(callback)
  }
}

API 被废弃

在 Android 中使用 overidePendingTransition () 方法实现进入和退出动画,但是在 Android 14 上提供了新的 overrideActivityTransition () 方法,而 overidePendingTransition () 方法已被标记为弃用。

// New API
overrideActivityTransition(
   enterAnim = R.anim.open_trans,
   exitAnim = R.anim.exit_trans,
   backgroundColor = R.color.bgr_color
)

// deprecated
overridePendingTransition(R.anim.open_trans, R.anim.exit_trans)


全文到这里就结束了,感谢你的阅读,坚持原创不易,欢迎 在看点赞分享 给身边的小伙伴,我会持续分享原创干货!!!

推荐阅读

字节:成员变量,局部变量,存放在哪里,为什么局部变量需要初始化

90% 的 Java 程序员都会犯的错
Android 14 新增权限
适配 Android 14,功能和权限的变更,你的应用受影响了吗


Hi 大家好,我是 DHL,大厂程序员,就职于 美团、快手、小米。分享技术干货和编程知识点,包含性能优化、系统源码、算法、数据结构、大厂面经。

  • 哔哩哔哩:https://space.bilibili.com/498153238

  • 掘金:https://juejin.im/user/2594503168898744

  • 博客:https://hi-dhl.com

  • Github:https://github.com/hi-dhl

👇🏻 真诚推荐你关注我👇🏻

因微信公众号更改了推送机制

可能无法及时看到最新文章

 将公众号设为 星标 

或常为文章点 在看

即可及时收到最新文章


欢迎前往 博客 查看更多 Kotlin、Jetpack 、动画算法图解、系统源码分析等等文章。以及开源项目、LeetCode / 剑指 offer / 国内外大厂面试题 / 多线程 题解。

https://www.hi-dhl.com

继续滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存